package com.killer.factory.impl;

import com.killer.factory.ApplicationContext;
import com.killer.pojo.BeanDefinition;
import com.killer.pojo.PropertyValue;
import com.killer.process.BeanPostProcessor;
import com.killer.utils.ClazzUtil;
import com.killer.utils.StringUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class AbstractApplicationContext implements ApplicationContext {

    /**
     * 单例池，用于保存对象，以供外部通过key(ID）获取
     */
    protected static Map<String,Object> singleton=new HashMap<String,Object>();

    protected static Map<String,Object> earlySingleton=new HashMap<String,Object>();
    /**
     * bean注册map,通过id可以获取bean定义对象
     */
    protected static Map<String, BeanDefinition> registerMap = new HashMap<String, BeanDefinition>();

    /**
     * 后置处理器列表
     */
    protected static List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>();

    public static BeanDefinition getBeanDefinition(String name) {
        return registerMap.get(name);
    }

    @Override
    public Object getBean(String name) throws Exception {
        //这里为了简化某些功能，没有参考spring那样用到三级缓存，只用到了二级缓存机制去解决循环依赖问题
        Object object = singleton.get(name);
        if (object == null) {
            //为空则在是否正在创建中
            object = earlySingleton.get(name);
            if (object == null) {
                //如果为空,则代表该对象没有创建(不考虑延迟加载)，需要创建并加入到容器中
                object = creatBean(name);
            }
        }
        return object;
    }

    protected void initializeBean() throws Exception {
        for (String name : registerMap.keySet()) {
            getBean(name);
        }
    }

    protected Object creatBean(String name) throws Exception {
        //先获取bean定义对象
        BeanDefinition beanDefinition = registerMap.get(name);
        if (beanDefinition == null) {
            throw new RuntimeException("该bean没有被定义");
        }
        //获取该bean的类型
        Class<?> clazz=beanDefinition.getClazz();
        //创建对象
        Object object = clazz.newInstance();
        List<String> beanIds = getBeanIds(name, clazz);
        //将该对象先提前暴露  这里没有参考spring用到三级缓存，只用到了二级缓存机制 主要为了解决循环依赖问题
        for (String beanId : beanIds) {
            earlySingleton.put(beanId, object);
        }
        //对属性进行赋值
        if (beanDefinition.getPropertyValues() != null) {
            for (PropertyValue propertyValue : beanDefinition.getPropertyValues()) {
                //获取字段名
                String fileName=propertyValue.getName();
                //直接给对象设值,这里就不用set方法注入了
                Field declaredField = clazz.getDeclaredField(fileName);
                declaredField.setAccessible(true);
                //查看ref属性是否为空，为空则代表是通过value赋值的
                if (propertyValue.getRef() == null) {
                    declaredField.set(object, propertyValue.getValue());
                }else{
                    //如果ref不为空，代表指向另一个bean
                    declaredField.set(object, getBean(propertyValue.getRef()));
                }
            }
        }
        //bean创建完成后就开始进行beanPostProcesser对bean进行加工处理
        object=doBeanPostProcess(object,name);
        //完成设置之后，对象可以说是创建完成了
        for (String beanId : beanIds) {
            earlySingleton.remove(beanId);
            //将其放入单例池
            singleton.put(beanId, object);
        }
        return object;
    }

    private Object doBeanPostProcess(Object object, String name) {
        Object object_=object;
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            object_=beanPostProcessor.postProcessAfterInitialization(object_, name);
        }
        return object_;
    }

    /**
     * 获取某个对象需要被那些名称作为key去映射该对象
     * @param name
     * @param clazz
     * @return
     */
    protected List<String> getBeanIds(String name, Class<?> clazz) {
        List<String> beanIds = new ArrayList<String>();
        //首先，name肯定要的
        beanIds.add(name);
        //同时如果有自定义id的话也要将类名小写保存一份，要不然通过autowired注解会找不到
        if (!name.equals(StringUtil.lowerFirstChar(clazz.getName()))) {
            beanIds.add(StringUtil.lowerFirstChar(clazz.getName()));
        }
        //该对象实现的接口也要
        Class<?>[] interfaces = clazz.getInterfaces();
        if (interfaces != null) {
            for (Class<?> i : interfaces) {
                beanIds.add(StringUtil.lowerFirstChar(i.getName()));
            }
        }

        return beanIds;
    }
}
